package com.dbflow5.query.property;

import com.dbflow5.config.FlowManager;
import com.dbflow5.query.ModelQueriable;
import com.dbflow5.query.NameAlias;
import com.dbflow5.query.Operator;

/**
 * Description: Provides some useful methods for creating [IProperty] from non-property types.
 */
public class PropertyFactory {

    /**
     * Converts a char into a [Property] as its value represented by string.
     *
     * @param c the char to convert.
     * @return A new property.
     */
    public static Property<Character> from(Character c) {
        return new Property<>(null, NameAlias.rawBuilder("'"+c+"'").build());
    }

    /**
     * Converts a int into a [Property] as its value represented by string.
     *
     * @param i the int to convert.
     * @return A new property.
     */
    public static Property<Integer> from(int i) {
        return new Property<>(null, NameAlias.rawBuilder(i + "").build());
    }

    /**
     * Converts a double into a [Property] as its value represented by string.
     *
     * @param d the double to convert.
     * @return A new property.
     */
    public static Property<Double> from(double d) {
        return new Property<>(null, NameAlias.rawBuilder(d + "").build());
    }

    /**
     * Converts a long into a [Property] as its value represented by string.
     *
     * @param l the long to convert.
     * @return A new property.
     */
    public static Property<Long> from(long l) {
        return new Property<>(null, NameAlias.rawBuilder(l + "").build());
    }

    /**
     * Converts a float into a [Property] as its value represented by string.
     *
     * @param f the float to convert.
     * @return A new property.
     */
    public static Property<Float> from(float f) {
        return new Property<>(null, NameAlias.rawBuilder(f + "").build());
    }

    /**
     * Converts a short into a [Property] as its value represented by string.
     *
     * @param s the short to convert.
     * @return A new property.
     */
    public static Property<Short> from(short s) {
        return new Property<>(null, NameAlias.rawBuilder(s + "").build());
    }

    /**
     * Converts a byte into a [Property] as its value represented by string.
     *
     * @param b the byte to convert.
     * @return A new property.
     */
    public static Property<Byte> from(byte b) {
        return new Property<>(null, NameAlias.rawBuilder(b + "").build());
    }

    /**
     * Creates a new type-parameterized [Property] to be used as its value represented by a string
     * using [Operator.convertValueToString].
     *
     *
     * It will not convert a String column name
     * into a property, rather it assumes its database value represented by the String.
     *
     * @param type The object with value to use.
     * @param <T>  The parameter of its type.
     * @return A new property with its type.
     */
    public static <T> Property<T>  from(T type) {
        return new Property<>(null, NameAlias.rawBuilder(Operator.convertValueToString(type) == null? "":Operator.convertValueToString(type)).build());
    }


    /**
     * Creates a new type-parameterized [Property] from a [NameAlias] directly.
     *
     * @param nameAlias will not be copied. Rather used directly.
     * @return [NameAlias] as a [Property]
     */
    public static Property<NameAlias> from(NameAlias nameAlias) {
        return new Property<>(null, nameAlias);
    }

    /**
     * Creates a new [Property] that is used to allow selects in a query.
     *
     * @param queriable The queriable to use and evaulated into a query.
     * @return A new property that is a query.
     */
    public static <T> Property<T> from(ModelQueriable<T> queriable) {
        return from(queriable.table(), "(" + queriable.getQuery().trim() + ")");
    }


    /**
     * Creates a new type-parameterized [Property] to be used as its value represented by the string passed in.
     *
     * @param table table
     * @param stringRepresentation The string representation of the object you wish to use.
     * @return A new property with its type.
     */
    public static <T> Property<T> from(Class<T> table, String stringRepresentation) {
        return new Property<>(null, NameAlias.rawBuilder(stringRepresentation == null? "":stringRepresentation).build());
    }

    public static Property<Integer> property(int i) {
        return PropertyFactory.from(i);
    }

    public static Property<Character> property(Character c) {
        return PropertyFactory.from(c);
    }

    public static Property<Double> property(double d) {
        return PropertyFactory.from(d);
    }

    public static Property<Long> property(long l) {
        return PropertyFactory.from(l);
    }

    public static Property<Float> property(float f) {
        return PropertyFactory.from(f);
    }

    public static Property<Short> property(short s) {
        return PropertyFactory.from(s);
    }

    public static Property<Byte> property(byte b) {
        return PropertyFactory.from(b);
    }

    public static Property<NameAlias> property(NameAlias nameAlias) {
        return PropertyFactory.from(nameAlias);
    }

    public static <T> Property<T> property(T t) {
        return PropertyFactory.from(t);
    }

    public static <T> Property<T> property(ModelQueriable<T> model) {
        return PropertyFactory.from(model);
    }


    public static <T> Property<T> propertyString(Class<T> clazz, String stringRepresentation) {
        return PropertyFactory.from(clazz, stringRepresentation);
    }

    /**
     * Convenience wrapper for creating a table name property used in queries.
     * @param clazz clazz
     * @param <T> T
     * @return A new property
     */
    public static <T> Property<T> tableName(Class<T> clazz) {
        return propertyString(clazz, FlowManager.getTableName(clazz));
    }

    /**
     * For FTS tables, "docid" is allowed as an alias along with the usual "rowid", "oid" and "_oid_" identifiers.
     * Attempting to insert or update a row with a docid value that already exists in the table is
     * an error, just as it would be with an ordinary SQLite table.
     * There is one other subtle difference between "docid" and the normal SQLite aliases for the rowid column.
     * Normally, if an INSERT or UPDATE statement assigns discrete values to two or more aliases of the rowid column, SQLite writes the rightmost of such values specified in the INSERT or UPDATE statement to the database. However, assigning a non-NULL value to both the "docid" and one or more of the SQLite rowid aliases when inserting or updating an FTS table is considered an error. See below for an example.
     */
    public static Property<Integer> docId = propertyString(Integer.class,"docid");
}


